#ifndef COMMANDSERVER_HPP
#define COMMANDSERVER_HPP

#include "doublebf2.hpp"
#include "server.hpp"

#include <vector>

class VehicleCommand
{
public:
    std::string clientID;
    std::string command;

    VehicleCommand()
    {

    }

    void Decode(std::string str)
    {
        command=str.substr(0,4);
        clientID=str.substr(5, str.size()-5);
    }
};


class VehicleStatus
{
public:

    //    possible status:
    //    TO=moving
    //    AT=idle
    //    LD=locker open, loading or unloading packages
    std::string status;
    //gate number
    std::string location;

    std::string lockerID[4];

    VehicleStatus()
        : status("AT")
        , location("10")
    {
        for(int i=0;i<4;i++) lockerID[i]="";
    }



    std::string Message(std::string clientID) const
    {
        std::string msg="";
        for(int i=0;i<4;i++)
        {
            if(lockerID[i].empty())
            {
                msg.push_back('0');
            }
            else
            {
                if(lockerID[i].compare(clientID)==0)
                {
                    msg.push_back('1');
                }
                else
                {
                    msg.push_back('2');
                }
            }
        }

        msg.push_back(status[0]);
        msg.push_back(status[1]);


        msg.push_back(location[0]);
        msg.push_back(location[1]);
        return msg;
    }

};


class VehicleLog
{
public:
    static std::string TimeString()
    {
        time_t rawtime;
        time(&rawtime);
        struct tm *timeinfo=localtime(&rawtime);
        char buffer[20];
        strftime(buffer, 20, "%Y%m%d_%H%M%S", timeinfo);
        return std::string(buffer);
    }

    std::vector<std::string> log;

    VehicleLog()
    {

    }

    void AddLog(std::string cmd)
    {
        if(log.size()>10)
        {
            log.erase(log.begin());
        }
        log.push_back(TimeString()+" "+cmd);
    }

    std::string LogStr() const
    {
        std::string str="";
        for(auto &s:log)
        {
            str+=s+"\n";
        }
        return str;
    }

    bool Load(std::string fp)
    {
        std::ifstream ifs(fp.c_str());
        if(ifs)
        {
            std::string ifn;
            while(!ifs.eof())
            {
                std::getline(ifs, ifn);
                if(!ifn.empty())
                    log.push_back(ifn);
            }
            ifs.close();
            return true;
        }
        return false;
    }

    bool Save(std::string fp)
    {
        std::ofstream ofs(fp.c_str());
        if(ofs)
        {
            for(auto &s:log)
            {
                ofs<<s<<"\n";
            }
            ofs.close();
            return true;
        }
        return false;
    }
};


class CommandServer : public ServerBase
{
public:
    DoubleBf2<VehicleCommand> command;
    std::thread listenThread;

    std::mutex statusLock;

    VehicleStatus status;




    VehicleLog log;

public:
    CommandServer()
        : ServerBase()
    {
        log.Load("log.txt");
    }





    //    command:
    //    TO##_****=go to ##
    //    OP##_****=open locker ##
    //    EQRY_****=enquiry status
    //    VLOG_****=download vehicle log
    virtual void task(char cmd[], std::string &response)
    {


        VehicleCommand *pcmd=new VehicleCommand();
        pcmd->Decode(std::string(cmd));

        if(pcmd->command.compare("VLOG")==0)
        {
            response=log.LogStr();
            delete pcmd;
            return;
        }

        if(pcmd->command.compare("EQRY")==0)
        {
            statusLock.lock();
            response=status.Message(pcmd->clientID);
            statusLock.unlock();

            delete pcmd;
            return;
        }

        std::string verb=pcmd->command.substr(0,2);

        if(verb.compare("TO")==0)
        {
            statusLock.lock();
            if(status.status.compare("AT")==0)
            {
                command.Write(pcmd);
                response="Y";
            }
            else
            {
                response="N";
                delete pcmd;
            }
            statusLock.unlock();
            return;
        }

        if(verb.compare("OP")==0)
        {
            int lockerIndex=-1;
            std::istringstream(pcmd->command.substr(2,2))>>lockerIndex;

            statusLock.lock();
            if(status.status.compare("AT")==0)
            {
                if(status.lockerID[lockerIndex].empty())
                {
                    command.Write(pcmd);
                    response="Y";
                }
                else
                {
                    if(status.lockerID[lockerIndex].compare(pcmd->clientID)==0)
                    {
                        command.Write(pcmd);
                        response="Y";
                    }
                    else
                    {
                        response="N locker not empty";
                        delete pcmd;
                    }
                }
            }
            else
            {
                response="N agv not ready";
                delete pcmd;
            }
            statusLock.unlock();

            return;
        }


        response="N command error";
        delete pcmd;

    }


    static void startThread(CommandServer *pData, uint16_t portno=4567)
    {
        pData->start(portno, 2048);
    }

    void Start(uint16_t portno=7654)
    {
        listenThread=std::thread(startThread, this, portno);
    }

    void End()
    {
        go=false;
        if(listenThread.joinable())
            listenThread.join();
    }


    void test()
    {
        Start();


        while(true)
        {
            VehicleCommand *pcmd=NULL;
            command.Read(pcmd);
            if(pcmd!=NULL)
            {
                std::string verb=pcmd->command.substr(0,2);

                if(verb.compare("TO")==0)
                {
                    statusLock.lock();
                    status.status=verb;
                    status.location=pcmd->command.substr(2,2);
                    statusLock.unlock();

                    std::cout<<"execute "<<pcmd->command<<"\n"<<std::flush;
                    std::this_thread::sleep_for(std::chrono::seconds(25));

                    statusLock.lock();
                    status.status="AT";
                    status.location=pcmd->command.substr(2,2);
                    statusLock.unlock();

                    std::cout<<"end execute "<<pcmd->command<<"\n"<<std::flush;
                }

                if(verb.compare("OP")==0)
                {
                    statusLock.lock();
                    status.status="LD";
                    statusLock.unlock();

                    std::cout<<"execute "<<pcmd->command<<"\n"<<std::flush;
                    std::this_thread::sleep_for(std::chrono::seconds(25));

                    statusLock.lock();
                    status.status="AT";

                    int lockerIndex=-1;
                    std::istringstream(pcmd->command.substr(2,2))>>lockerIndex;
                    if(status.lockerID[lockerIndex].empty())
                        status.lockerID[lockerIndex]=pcmd->clientID;
                    else
                        status.lockerID[lockerIndex]="";
                    statusLock.unlock();
                    std::cout<<"end execute "<<pcmd->command<<"\n"<<std::flush;

                }
            }
        }

        End();

    }

};



#endif // COMMANDSERVER_HPP

